Import data
data.path = "/Volumes/rdss_bhe2/User/Hanxi Tang/Flow Cytometry/092923-FungaLight-Cgla-analysis/"
fs <- read.flowSet(path = data.path, transformation = FALSE, # the original values are already linearized.
emptyValue = FALSE, alter.names = TRUE, # change parameter names to R format
column.pattern = ".H|FSC|SSC") # only load the height variables for the fluorescent parameters
fs0 <- fs # make a copy of the original data in case of corruption
Specify the flowSet phenoData
source("../00-shared/script/20220326-simplify-names-subroutine.R")
oriNames <- sampleNames(fs)
tmp <- str_split(oriNames, pattern = "_", simplify = TRUE)[,c(1, 5)]
colnames(tmp) <- c("date", "treat")
sample <- data.frame(tmp) %>%
mutate(rep = ifelse(str_sub(treat, 1, 1) == "B", "B", "A"),
treat = gsub(".fcs", "", treat) %>% gsub("^B ", "", .),
H2O2 = case_match(treat,
"1M" ~ "1000",
"Mock" ~ "0",
.default = treat),
H2O2 = factor(H2O2, levels = sort(unique(as.numeric(H2O2)))),
name = paste(date, rep, H2O2, "mM", sep = "_"))
rownames(sample) <- oriNames
pData(fs) <- sample
Exploratory Data Analysis
Let’s take one dataset and visualize the changes in the various
parameters
# define the rectangular gate
cell.filter <- rectangleGate(filterId = "cell",
"FSC.H" = c(125E3, 100E4),
"SSC.H" = c(0, 750E3))
Plot all sets
for(set in sets){
sub <- fs[pData(fs)$set == set]
# constructor for a list of filters
#cell.gates <- sapply(sampleNames(fs), function(sn)cell.filter)
p1 <- ggcyto(sub, aes(x = "FSC.H", y = "SSC.H")) + geom_hex(bins = 80) +
geom_gate(cell.filter) +
facet_wrap(~H2O2, labeller = "label_both") + theme_minimal() +
labs(title = set) +
scale_y_continuous(name = "SSC.H (x1000)", labels = function(l) {l/1000}) +
scale_x_continuous(name = "FSC.H (x1000)", labels = function(l) {l/1000})
print(p1)
}






Two populations are visible in the FSC:SSC plot. The minor population
with smaller SSC values are more prominent in the more severely stressed
cells.
Try Identifying the two clusters
tmp.dat <- fs[pData(fs)$set == "091523A" & pData(fs)$H2O2 == 8][[1]]
tmp.res <- flowClust(
tmp.dat,
varNames = c("FSC.H", "SSC.H"),
K = 2,
B = 500
)
Using the serial version of flowClust
summary(tmp.res)
** Experiment Information **
Experiment name: Flow Experiment
Variables used: FSC.H SSC.H
** Clustering Summary **
Number of clusters: 2
Proportions: 0.7342262 0.2657738
** Transformation Parameter **
lambda: 0.2889299
** Information Criteria **
Log likelihood: -525766.8
BIC: -1051653
ICL: -1057529
** Data Quality **
Number of points filtered from above: 0 (0%)
Number of points filtered from below: 0 (0%)
Rule of identifying outliers: 90% quantile
Number of outliers: 999 (4.82%)
Uncertainty summary:
def.par <- par(no.readonly = TRUE) # save defaults, for resetting...
layout(matrix(c(1,2), ncol = 2)) # divide the figure into two columns
plot(tmp.res, data = tmp.dat, level = 0.8, z.cutoff = 0)
Rule of identifying outliers: 80% quantile
plot(density(tmp.res, data = sub[[4]]), type = "image")
par(def.par)

Let’s split the two populations and compare their fluorescence
levels
# creates a filter object to store all settings, but doesn't perform the clustering
tmp.filter <- tmixFilter(filterId = "fsc-ssc", parameters = c("FSC.H", "SSC.H"), K = 2, B = 500)
# implement the actual clustering
tmp.res2 <- filter(tmp.dat, tmp.filter)
The prior specification has no effect when usePrior=no
Using the serial version of flowClust
tmp.split <- split(tmp.dat, tmp.res2, population = list(ssc.h = 1, ssc.l = 2)) %>% as("flowSet")
# get the MFI for each subset
tmp.median <- fsApply(tmp.split, each_col, median)
tmp.median
FSC.A SSC.A FSC.H SSC.H BL1.H BL3.H FSC.W SSC.W
ssc.h 381458 283287.0 376180 277454.0 480 172 83 83
ssc.l 337802 150177.5 338860 149218.5 294 1159 81 79
ggcyto(tmp.split, aes(x = "FSC.H", y = "SSC.H")) + geom_hex(bins = 80) + theme_minimal()

ggcyto(tmp.split, aes(x = "BL1.H", y = "BL3.H")) + geom_hex(bins = 80) + scale_x_logicle() + scale_y_logicle() +
theme_minimal()

Interestingly, the SSC High population appear to be live and SSC low
population are mostly dead Also of interest is the presence of a
subpopulation with very low staining by FungaLight. This population is
mostly in the SSC-high, thus live portion of the sample.
To examine the low-staining pop further, I will make a gate on BL1.H
and BL3.H
unstained.filter <- rectangleGate(filterId = "unstained",
list("BL1.H" = c(0, 100),
"BL3.H" = c(0, 100)))
unstained.res <- filter(tmp.dat, unstained.filter)
unstained.pop <- split(tmp.dat, unstained.res) %>% as("flowSet")
ggcyto(unstained.pop, aes(x = "FSC.H")) + geom_density(aes(color = factor(name)), linewidth = 1.5) +
scale_color_manual("Unstained", values = c("unstained-" = "gray20", "unstained+" = "red")) +
facet_wrap(~NULL) + theme_minimal(base_size = 16)

The unstained population of events seem to be a bit smaller in size
compared with the stained population. wonder if this has to do with a
transient population of cells with physiological characteristics that
make them both smaller and less permeable to both dyes.
for(set in sets){
sub <- fs[pData(fs)$set == set]
# constructor for a list of filters
#cell.gates <- sapply(sampleNames(fs), function(sn)cell.filter)
p2 <- ggcyto(sub, aes(x = BL1.H, y = BL3.H)) +
geom_hex(aes(fill = after_stat(ncount)), bins = 80) +
facet_wrap(~H2O2, labeller = "label_both") +
scale_y_logicle() + scale_x_logicle() +
labs(title = set) +
theme_minimal()
print(p2)
}






We can observe some trends with increasing H2O2 severity, such as an
increase in the dead population (top left), more cells exhibiting a
higher green and red within the “live” population, possibly due to
increased permeability to SYTO9, which is much more fluorescent than PI.
Eventually, the population does migrate to the dead gate. Despite these
useful trends however, there is a lot of run-to-run variability, to the
point where confident assessment of the patterns is not possible.
LS0tCnRpdGxlOiAiRmlnIDMgR3JhcGhzIgphdXRob3I6IEJpbiBIZSwgb3JpZ2luYWxseSBieSBIYW54aSBUYW5nCmRhdGU6ICIyMDIzLTA5LTMwICh1cGRhdGVkIGByIFN5cy5EYXRlKClgKSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0aGVtZTogY2VydWxlYW4KICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDQKICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KCiMgQmFja2dyb3VuZApUaGlzIGlzIHRoZSBmbG93IGN5dG9tZXRyeSBhbmQgQ0ZVIGRhdGEgZm9yIENnLCBTYywgS2wuIENnLCBTYywgS2wgdHJlYXRlZCB3aXRoIDAtMU0gSDJPMiBhcmUgc3RhaW5lZCB3aXRoIEZ1bmdhbGlnaHQgMSwgcnVuIHRocm91Z2ggZmxvdyBhbmQgcGxhdGVkIGZvciBDRlUgKGRldGFpbHMgaW4gSFQncyBFTE4pLiBHb2FsIGlzIHRvIGNvcnJlbGF0ZSBGbG93IHZzIENGVSBhbmQgZ2VuZXJhdGUgZ3JhcGhzIGZvciBmaWd1cmUgMy4KCmBgYHtyIHNldHVwLCBtZXNzYWdlPUZBTFNFfQpyZXF1aXJlKHRpZHl2ZXJzZSkKcmVxdWlyZShmbG93Q29yZSkKcmVxdWlyZShmbG93Q2x1c3QpCnJlcXVpcmUob3BlbkN5dG8pCnJlcXVpcmUoZ2djeXRvKQpyZXF1aXJlKGNvd3Bsb3QpCmBgYAoKIyBJbXBvcnQgZGF0YQpgYGB7cn0KZGF0YS5wYXRoID0gIi9Wb2x1bWVzL3Jkc3NfYmhlMi9Vc2VyL0hhbnhpIFRhbmcvRmxvdyBDeXRvbWV0cnkvMDkyOTIzLUZ1bmdhTGlnaHQtQ2dsYS1hbmFseXNpcy8iCmZzIDwtIHJlYWQuZmxvd1NldChwYXRoID0gZGF0YS5wYXRoLCB0cmFuc2Zvcm1hdGlvbiA9IEZBTFNFLCAgIyB0aGUgb3JpZ2luYWwgdmFsdWVzIGFyZSBhbHJlYWR5IGxpbmVhcml6ZWQuIAogICAgICAgICAgICAgICAgICAgZW1wdHlWYWx1ZSA9IEZBTFNFLCAgYWx0ZXIubmFtZXMgPSBUUlVFLCAgICMgY2hhbmdlIHBhcmFtZXRlciBuYW1lcyB0byBSIGZvcm1hdAogICAgICAgICAgICAgICAgICAgY29sdW1uLnBhdHRlcm4gPSAiLkh8RlNDfFNTQyIpICMgb25seSBsb2FkIHRoZSBoZWlnaHQgdmFyaWFibGVzIGZvciB0aGUgZmx1b3Jlc2NlbnQgcGFyYW1ldGVycwpmczAgPC0gZnMgIyBtYWtlIGEgY29weSBvZiB0aGUgb3JpZ2luYWwgZGF0YSBpbiBjYXNlIG9mIGNvcnJ1cHRpb24KYGBgCgpTcGVjaWZ5IHRoZSBmbG93U2V0IHBoZW5vRGF0YQpgYGB7cn0Kc291cmNlKCIuLi8wMC1zaGFyZWQvc2NyaXB0LzIwMjIwMzI2LXNpbXBsaWZ5LW5hbWVzLXN1YnJvdXRpbmUuUiIpCm9yaU5hbWVzIDwtIHNhbXBsZU5hbWVzKGZzKQp0bXAgPC0gc3RyX3NwbGl0KG9yaU5hbWVzLCBwYXR0ZXJuID0gIl8iLCBzaW1wbGlmeSA9IFRSVUUpWyxjKDEsIDUpXSAKY29sbmFtZXModG1wKSA8LSBjKCJkYXRlIiwgInRyZWF0IikgCnNhbXBsZSA8LSBkYXRhLmZyYW1lKHRtcCkgJT4lIAogIG11dGF0ZShyZXAgPSBpZmVsc2Uoc3RyX3N1Yih0cmVhdCwgMSwgMSkgPT0gIkIiLCAiQiIsICJBIiksCiAgICAgICAgIHRyZWF0ID0gZ3N1YigiLmZjcyIsICIiLCB0cmVhdCkgJT4lIGdzdWIoIl5CICIsICIiLCAuKSwKICAgICAgICAgSDJPMiA9IGNhc2VfbWF0Y2godHJlYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjFNIiB+ICIxMDAwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTW9jayIgfiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLmRlZmF1bHQgPSB0cmVhdCksCiAgICAgICAgIEgyTzIgPSBmYWN0b3IoSDJPMiwgbGV2ZWxzID0gc29ydCh1bmlxdWUoYXMubnVtZXJpYyhIMk8yKSkpKSwKICAgICAgICAgc2V0ID0gZmFjdG9yKHBhc3RlMChkYXRlLCByZXApKSwKICAgICAgICAgbmFtZSA9IHBhc3RlKGRhdGUsIHJlcCwgSDJPMiwgIm1NIiwgc2VwID0gIl8iKSkKcm93bmFtZXMoc2FtcGxlKSA8LSBvcmlOYW1lcwpwRGF0YShmcykgPC0gc2FtcGxlCnNldHMgPC0gbGV2ZWxzKHNhbXBsZSRzZXQpCmBgYAoKIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzCkxldCdzIHRha2Ugb25lIGRhdGFzZXQgYW5kIHZpc3VhbGl6ZSB0aGUgY2hhbmdlcyBpbiB0aGUgdmFyaW91cyBwYXJhbWV0ZXJzCgpgYGB7cn0KIyBkZWZpbmUgdGhlIHJlY3Rhbmd1bGFyIGdhdGUKY2VsbC5maWx0ZXIgPC0gcmVjdGFuZ2xlR2F0ZShmaWx0ZXJJZCA9ICJjZWxsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZTQy5IIiA9IGMoMTI1RTMsIDEwMEU0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU1NDLkgiID0gYygwLCA3NTBFMykpCmBgYAoKUGxvdCBhbGwgc2V0cwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KcG5nKCJpbWcvMjAyMzA5MzAtIikKZm9yKHNldCBpbiBzZXRzKXsKICBzdWIgPC0gZnNbcERhdGEoZnMpJHNldCA9PSBzZXRdCiAgIyBjb25zdHJ1Y3RvciBmb3IgYSBsaXN0IG9mIGZpbHRlcnMKICAjY2VsbC5nYXRlcyA8LSBzYXBwbHkoc2FtcGxlTmFtZXMoZnMpLCBmdW5jdGlvbihzbiljZWxsLmZpbHRlcikKICBwMSA8LSBnZ2N5dG8oc3ViLCBhZXMoeCA9ICJGU0MuSCIsIHkgPSAiU1NDLkgiKSkgKyBnZW9tX2hleChiaW5zID0gODApICsgCiAgICBnZW9tX2dhdGUoY2VsbC5maWx0ZXIpICsKICAgIGZhY2V0X3dyYXAofkgyTzIsIGxhYmVsbGVyID0gImxhYmVsX2JvdGgiKSArIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKHRpdGxlID0gc2V0KSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJTU0MuSCAoeDEwMDApIiwgbGFiZWxzID0gZnVuY3Rpb24obCkge2wvMTAwMH0pICsgCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICJGU0MuSCAoeDEwMDApIiwgbGFiZWxzID0gZnVuY3Rpb24obCkge2wvMTAwMH0pCiAgcHJpbnQocDEpCn0KYGBgCgo+IFR3byBwb3B1bGF0aW9ucyBhcmUgdmlzaWJsZSBpbiB0aGUgRlNDOlNTQyBwbG90LiBUaGUgbWlub3IgcG9wdWxhdGlvbiB3aXRoIHNtYWxsZXIgU1NDIHZhbHVlcyBhcmUgbW9yZSBwcm9taW5lbnQgaW4gdGhlIG1vcmUgc2V2ZXJlbHkgc3RyZXNzZWQgY2VsbHMuCgpUcnkgSWRlbnRpZnlpbmcgdGhlIHR3byBjbHVzdGVycwpgYGB7cn0KdG1wLmRhdCA8LSBmc1twRGF0YShmcykkc2V0ID09ICIwOTE1MjNBIiAmIHBEYXRhKGZzKSRIMk8yID09IDhdW1sxXV0KdG1wLnJlcyA8LSBmbG93Q2x1c3QoCiAgdG1wLmRhdCwKICB2YXJOYW1lcyA9IGMoIkZTQy5IIiwgIlNTQy5IIiksCiAgSyA9IDIsCiAgQiA9IDUwMAogICkKYGBgCgpgYGB7cn0Kc3VtbWFyeSh0bXAucmVzKQpkZWYucGFyIDwtIHBhcihuby5yZWFkb25seSA9IFRSVUUpICMgc2F2ZSBkZWZhdWx0cywgZm9yIHJlc2V0dGluZy4uLgpsYXlvdXQobWF0cml4KGMoMSwyKSwgbmNvbCA9IDIpKSAgICMgZGl2aWRlIHRoZSBmaWd1cmUgaW50byB0d28gY29sdW1ucwpwbG90KHRtcC5yZXMsIGRhdGEgPSB0bXAuZGF0LCBsZXZlbCA9IDAuOCwgei5jdXRvZmYgPSAwKQpwbG90KGRlbnNpdHkodG1wLnJlcywgZGF0YSA9IHN1YltbNF1dKSwgdHlwZSA9ICJpbWFnZSIpCnBhcihkZWYucGFyKQpgYGAKCkxldCdzIHNwbGl0IHRoZSB0d28gcG9wdWxhdGlvbnMgYW5kIGNvbXBhcmUgdGhlaXIgZmx1b3Jlc2NlbmNlIGxldmVscwpgYGB7cn0KIyBjcmVhdGVzIGEgZmlsdGVyIG9iamVjdCB0byBzdG9yZSBhbGwgc2V0dGluZ3MsIGJ1dCBkb2Vzbid0IHBlcmZvcm0gdGhlIGNsdXN0ZXJpbmcKdG1wLmZpbHRlciA8LSB0bWl4RmlsdGVyKGZpbHRlcklkID0gImZzYy1zc2MiLCBwYXJhbWV0ZXJzID0gYygiRlNDLkgiLCAiU1NDLkgiKSwgSyA9IDIsIEIgPSA1MDApCiMgaW1wbGVtZW50IHRoZSBhY3R1YWwgY2x1c3RlcmluZyAKdG1wLnJlczIgPC0gZmlsdGVyKHRtcC5kYXQsIHRtcC5maWx0ZXIpCmBgYAoKYGBge3J9CnRtcC5zcGxpdCA8LSBzcGxpdCh0bXAuZGF0LCB0bXAucmVzMiwgcG9wdWxhdGlvbiA9IGxpc3Qoc3NjLmggPSAxLCBzc2MubCA9IDIpKSAlPiUgYXMoImZsb3dTZXQiKQojIGdldCB0aGUgTUZJIGZvciBlYWNoIHN1YnNldAp0bXAubWVkaWFuIDwtIGZzQXBwbHkodG1wLnNwbGl0LCBlYWNoX2NvbCwgbWVkaWFuKQp0bXAubWVkaWFuCmdnY3l0byh0bXAuc3BsaXQsIGFlcyh4ID0gIkZTQy5IIiwgeSA9ICJTU0MuSCIpKSArIGdlb21faGV4KGJpbnMgPSA4MCkgKyB0aGVtZV9taW5pbWFsKCkKZ2djeXRvKHRtcC5zcGxpdCwgYWVzKHggPSAiQkwxLkgiLCB5ID0gIkJMMy5IIikpICsgZ2VvbV9oZXgoYmlucyA9IDgwKSArIHNjYWxlX3hfbG9naWNsZSgpICsgc2NhbGVfeV9sb2dpY2xlKCkgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKPiBJbnRlcmVzdGluZ2x5LCB0aGUgU1NDIEhpZ2ggcG9wdWxhdGlvbiBhcHBlYXIgdG8gYmUgbGl2ZSBhbmQgU1NDIGxvdyBwb3B1bGF0aW9uIGFyZSBtb3N0bHkgZGVhZAo+IEFsc28gb2YgaW50ZXJlc3QgaXMgdGhlIHByZXNlbmNlIG9mIGEgc3VicG9wdWxhdGlvbiB3aXRoIHZlcnkgbG93IHN0YWluaW5nIGJ5IEZ1bmdhTGlnaHQuIFRoaXMgcG9wdWxhdGlvbiBpcyBtb3N0bHkgaW4gdGhlIFNTQy1oaWdoLCB0aHVzIGxpdmUgcG9ydGlvbiBvZiB0aGUgc2FtcGxlLgoKVG8gZXhhbWluZSB0aGUgbG93LXN0YWluaW5nIHBvcCBmdXJ0aGVyLCBJIHdpbGwgbWFrZSBhIGdhdGUgb24gQkwxLkggYW5kIEJMMy5ICmBgYHtyfQp1bnN0YWluZWQuZmlsdGVyIDwtIHJlY3RhbmdsZUdhdGUoZmlsdGVySWQgPSAidW5zdGFpbmVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KCJCTDEuSCIgPSBjKDAsIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCTDMuSCIgPSBjKDAsIDEwMCkpKQp1bnN0YWluZWQucmVzIDwtIGZpbHRlcih0bXAuZGF0LCB1bnN0YWluZWQuZmlsdGVyKQp1bnN0YWluZWQucG9wIDwtIHNwbGl0KHRtcC5kYXQsIHVuc3RhaW5lZC5yZXMpICU+JSBhcygiZmxvd1NldCIpCmdnY3l0byh1bnN0YWluZWQucG9wLCBhZXMoeCA9ICJGU0MuSCIpKSArIGdlb21fZGVuc2l0eShhZXMoY29sb3IgPSBmYWN0b3IobmFtZSkpLCBsaW5ld2lkdGggPSAxLjUpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKCJVbnN0YWluZWQiLCB2YWx1ZXMgPSBjKCJ1bnN0YWluZWQtIiA9ICJncmF5MjAiLCAidW5zdGFpbmVkKyIgPSAicmVkIikpICsKICBmYWNldF93cmFwKH5OVUxMKSArIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTYpCmBgYAo+IFRoZSB1bnN0YWluZWQgcG9wdWxhdGlvbiBvZiBldmVudHMgc2VlbSB0byBiZSBhIGJpdCBzbWFsbGVyIGluIHNpemUgY29tcGFyZWQgd2l0aCB0aGUgc3RhaW5lZCBwb3B1bGF0aW9uLgo+IHdvbmRlciBpZiB0aGlzIGhhcyB0byBkbyB3aXRoIGEgdHJhbnNpZW50IHBvcHVsYXRpb24gb2YgY2VsbHMgd2l0aCBwaHlzaW9sb2dpY2FsIGNoYXJhY3RlcmlzdGljcwo+IHRoYXQgbWFrZSB0aGVtIGJvdGggc21hbGxlciBhbmQgbGVzcyBwZXJtZWFibGUgdG8gYm90aCBkeWVzLgoKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CmZvcihzZXQgaW4gc2V0cyl7CiAgc3ViIDwtIGZzW3BEYXRhKGZzKSRzZXQgPT0gc2V0XQogICMgY29uc3RydWN0b3IgZm9yIGEgbGlzdCBvZiBmaWx0ZXJzCiAgI2NlbGwuZ2F0ZXMgPC0gc2FwcGx5KHNhbXBsZU5hbWVzKGZzKSwgZnVuY3Rpb24oc24pY2VsbC5maWx0ZXIpCiAgcDIgPC0gZ2djeXRvKHN1YiwgYWVzKHggPSBCTDEuSCwgeSA9IEJMMy5IKSkgKyAKICAgIGdlb21faGV4KGFlcyhmaWxsID0gYWZ0ZXJfc3RhdChuY291bnQpKSwgYmlucyA9IDgwKSArIAogICAgZmFjZXRfd3JhcCh+SDJPMiwgbGFiZWxsZXIgPSAibGFiZWxfYm90aCIpICsKICAgIHNjYWxlX3lfbG9naWNsZSgpICsgc2NhbGVfeF9sb2dpY2xlKCkgKwogICAgbGFicyh0aXRsZSA9IHNldCkgKwogICAgdGhlbWVfbWluaW1hbCgpCiAgcHJpbnQocDIpCn0KYGBgCj4gV2UgY2FuIG9ic2VydmUgc29tZSB0cmVuZHMgd2l0aCBpbmNyZWFzaW5nIEgyTzIgc2V2ZXJpdHksIHN1Y2ggYXMgYW4gaW5jcmVhc2UgaW4gdGhlIAo+IGRlYWQgcG9wdWxhdGlvbiAodG9wIGxlZnQpLCBtb3JlIGNlbGxzIGV4aGliaXRpbmcgYSBoaWdoZXIgZ3JlZW4gYW5kIHJlZCB3aXRoaW4gdGhlICJsaXZlIgo+IHBvcHVsYXRpb24sIHBvc3NpYmx5IGR1ZSB0byBpbmNyZWFzZWQgcGVybWVhYmlsaXR5IHRvIFNZVE85LCB3aGljaCBpcyBtdWNoIG1vcmUgZmx1b3Jlc2NlbnQKPiB0aGFuIFBJLiBFdmVudHVhbGx5LCB0aGUgcG9wdWxhdGlvbiBkb2VzIG1pZ3JhdGUgdG8gdGhlIGRlYWQgZ2F0ZS4gRGVzcGl0ZSB0aGVzZSB1c2VmdWwgdHJlbmRzCj4gaG93ZXZlciwgdGhlcmUgaXMgYSBsb3Qgb2YgcnVuLXRvLXJ1biB2YXJpYWJpbGl0eSwgdG8gdGhlIHBvaW50IHdoZXJlIGNvbmZpZGVudCBhc3Nlc3NtZW50IG9mCj4gdGhlIHBhdHRlcm5zIGlzIG5vdCBwb3NzaWJsZS4KCg==